home *** CD-ROM | disk | FTP | other *** search
- /* cp.c -- file copying (main routines)
- Copyright (C) 89, 90, 91, 95, 1996 Free Software Foundation.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software Foundation,
- Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
- Written by Torbjorn Granlund, David MacKenzie, and Jim Meyering. */
-
- #ifdef _AIX
- #pragma alloca
- #endif
-
- #include <config.h>
- #include <stdio.h>
-
- #define NDEBUG
- #include <assert.h>
-
- #include <getopt.h>
- #include "cp.h"
- #include "backupfile.h"
- #include "argmatch.h"
-
- #ifndef _POSIX_VERSION
- uid_t geteuid ();
- #endif
-
- #ifdef HAVE_LCHOWN
- # define chown(PATH, OWNER, GROUP) lchown(PATH, OWNER, GROUP)
- #endif
-
- /* Used by do_copy, make_path_private, and re_protect
- to keep a list of leading directories whose protections
- need to be fixed after copying. */
- struct dir_attr
- {
- int is_new_dir;
- int slash_offset;
- struct dir_attr *next;
- };
-
- /* Control creation of sparse files (files with holes). */
- enum Sparse_type
- {
- /* Never create holes in DEST. */
- SPARSE_NEVER,
-
- /* This is the default. Use a crude (and sometimes inaccurate)
- heuristic to determine if SOURCE has holes. If so, try to create
- holes in DEST. */
- SPARSE_AUTO,
-
- /* For every sufficiently long sequence of bytes in SOURCE, try to
- create a corresponding hole in DEST. There is a performance penalty
- here because CP has to search for holes in SRC. But if the holes are
- big enough, that penalty can be offset by the decrease in the amount
- of data written to disk. */
- SPARSE_ALWAYS
- };
-
- int stat ();
- int lstat ();
-
- char *dirname ();
- char *xstrdup ();
- enum backup_type get_version ();
- int euidaccess ();
- int full_write ();
-
- static int do_copy __P ((int argc, char **argv));
- static int copy __P ((const char *src_path, const char *dst_path, int new_dst,
- dev_t device, struct dir_list *ancestors));
- static int copy_dir __P ((const char *src_path_in, const char *dst_path_in,
- int new_dst, const struct stat *src_sb,
- struct dir_list *ancestors));
- static int make_path_private __P ((const char *const_dirpath, int src_offset,
- int mode, const char *verbose_fmt_string,
- struct dir_attr **attr_list, int *new_dst));
- static int copy_reg __P ((const char *src_path, const char *dst_path));
- static int re_protect __P ((const char *const_dst_path, int src_offset,
- struct dir_attr *attr_list));
-
- /* Initial number of entries in each hash table entry's table of inodes. */
- #define INITIAL_HASH_MODULE 100
-
- /* Initial number of entries in the inode hash table. */
- #define INITIAL_ENTRY_TAB_SIZE 70
-
- /* The invocation name of this program. */
- char *program_name;
-
- /* A pointer to either lstat or stat, depending on
- whether dereferencing of symlinks is done. */
- static int (*xstat) ();
-
- /* If nonzero, copy all files except (directories and, if not dereferencing
- them, symbolic links,) as if they were regular files. */
- static int flag_copy_as_regular = 1;
-
- /* If nonzero, dereference symbolic links (copy the files they point to). */
- static int flag_dereference = 1;
-
- /* If nonzero, remove existing destination nondirectories. */
- static int flag_force = 0;
-
- /* If nonzero, create hard links instead of copying files.
- Create destination directories as usual. */
- static int flag_hard_link = 0;
-
- /* If nonzero, query before overwriting existing destinations
- with regular files. */
- static int flag_interactive = 0;
-
- /* If nonzero, the command "cp x/e_file e_dir" uses "e_dir/x/e_file"
- as its destination instead of the usual "e_dir/e_file." */
- static int flag_path = 0;
-
- /* If nonzero, give the copies the original files' permissions,
- ownership, and timestamps. */
- static int flag_preserve = 0;
-
- /* If nonzero, copy directories recursively and copy special files
- as themselves rather than copying their contents. */
- static int flag_recursive = 0;
-
- /* If nonzero, create symbolic links instead of copying files.
- Create destination directories as usual. */
- static int flag_symbolic_link = 0;
-
- /* If nonzero, when copying recursively, skip any subdirectories that are
- on different filesystems from the one we started on. */
- static int flag_one_file_system = 0;
-
- /* If nonzero, do not copy a nondirectory that has an existing destination
- with the same or newer modification time. */
- static int flag_update = 0;
-
- /* If nonzero, display the names of the files before copying them. */
- static int flag_verbose = 0;
-
- static char const *const sparse_type_string[] =
- {
- "never", "auto", "always", 0
- };
-
- static enum Sparse_type const sparse_type[] =
- {
- SPARSE_NEVER, SPARSE_AUTO, SPARSE_ALWAYS
- };
-
- /* Control creation of sparse files. */
- static int flag_sparse = SPARSE_AUTO;
-
- /* The error code to return to the system. */
- static int exit_status = 0;
-
- /* The bits to preserve in created files' modes. */
- static int umask_kill;
-
- /* This process's effective user ID. */
- static uid_t myeuid;
-
- /* If nonzero, display usage information and exit. */
- static int show_help;
-
- /* If nonzero, print the version on standard output and exit. */
- static int show_version;
-
- static struct option const long_opts[] =
- {
- {"archive", no_argument, NULL, 'a'},
- {"backup", no_argument, NULL, 'b'},
- {"force", no_argument, NULL, 'f'},
- {"sparse", required_argument, NULL, 2},
- {"interactive", no_argument, NULL, 'i'},
- {"link", no_argument, NULL, 'l'},
- {"no-dereference", no_argument, &flag_dereference, 0},
- {"one-file-system", no_argument, &flag_one_file_system, 1},
- {"parents", no_argument, &flag_path, 1},
- {"path", no_argument, &flag_path, 1},
- {"preserve", no_argument, &flag_preserve, 1},
- {"recursive", no_argument, NULL, 'R'},
- {"suffix", required_argument, NULL, 'S'},
- {"symbolic-link", no_argument, NULL, 's'},
- {"update", no_argument, &flag_update, 1},
- {"verbose", no_argument, &flag_verbose, 1},
- {"version-control", required_argument, NULL, 'V'},
- {"help", no_argument, &show_help, 1},
- {"version", no_argument, &show_version, 1},
- {NULL, 0, NULL, 0}
- };
-
- int
- main (int argc, char **argv)
- {
- int c;
- int make_backups = 0;
- char *version;
-
- program_name = argv[0];
- setlocale (LC_ALL, "");
- bindtextdomain (PACKAGE, LOCALEDIR);
- textdomain (PACKAGE);
-
- myeuid = geteuid ();
-
- version = getenv ("SIMPLE_BACKUP_SUFFIX");
- if (version)
- simple_backup_suffix = version;
- version = getenv ("VERSION_CONTROL");
-
- /* Find out the current file creation mask, to knock the right bits
- when using chmod. The creation mask is set to to be liberal, so
- that created directories can be written, even if it would not
- have been allowed with the mask this process was started with. */
-
- umask_kill = 0777777 ^ umask (0);
-
- while ((c = getopt_long (argc, argv, "abdfilprsuvxPRS:V:", long_opts,
- (int *) 0)) != EOF)
- {
- switch (c)
- {
- case 0:
- break;
-
- case 2:
- {
- int i;
-
- /* --sparse={never,auto,always} */
- i = argmatch (optarg, sparse_type_string);
- if (i < 0)
- {
- invalid_arg (_("sparse type"), optarg, i);
- usage (2, NULL);
- }
- flag_sparse = sparse_type[i];
- }
- break;
-
- case 'a': /* Like -dpR. */
- flag_dereference = 0;
- flag_preserve = 1;
- flag_recursive = 1;
- flag_copy_as_regular = 0;
- break;
-
- case 'b':
- make_backups = 1;
- break;
-
- case 'd':
- flag_dereference = 0;
- break;
-
- case 'f':
- flag_force = 1;
- flag_interactive = 0;
- break;
-
- case 'i':
- flag_force = 0;
- flag_interactive = 1;
- break;
-
- case 'l':
- flag_hard_link = 1;
- break;
-
- case 'p':
- flag_preserve = 1;
- break;
-
- case 'P':
- flag_path = 1;
- break;
-
- case 'r':
- flag_recursive = 1;
- flag_copy_as_regular = 1;
- break;
-
- case 'R':
- flag_recursive = 1;
- flag_copy_as_regular = 0;
- break;
-
- case 's':
- #ifdef S_ISLNK
- flag_symbolic_link = 1;
- #else
- error (1, 0, _("symbolic links are not supported on this system"));
- #endif
- break;
-
- case 'u':
- flag_update = 1;
- break;
-
- case 'v':
- flag_verbose = 1;
- break;
-
- case 'x':
- flag_one_file_system = 1;
- break;
-
- case 'S':
- simple_backup_suffix = optarg;
- break;
-
- case 'V':
- version = optarg;
- break;
-
- default:
- usage (2, (char *) 0);
- }
- }
-
- if (show_version)
- {
- printf ("cp - %s\n", PACKAGE_VERSION);
- exit (0);
- }
-
- if (show_help)
- usage (0, NULL);
-
- if (flag_hard_link && flag_symbolic_link)
- usage (2, _("cannot make both hard and symbolic links"));
-
- if (make_backups)
- backup_type = get_version (version);
-
- if (flag_preserve == 1)
- umask_kill = 0777777;
-
- /* The key difference between -d (--no-dereference) and not is the version
- of `stat' to call. */
-
- if (flag_dereference)
- xstat = stat;
- else
- xstat = lstat;
-
- /* Allocate space for remembering copied and created files. */
-
- hash_init (INITIAL_HASH_MODULE, INITIAL_ENTRY_TAB_SIZE);
-
- exit_status |= do_copy (argc, argv);
-
- exit (exit_status);
- }
-
- /* Concatenate two pathname components, DIR and BASE, in newly-allocated
- storage and return the result. Be careful that in the result they are
- separated by a slash. That is, if DIR ends with a slash or if BASE
- begins with one, don't add a separating slash. Otherwise, add one. */
-
- static char *
- path_concat (const char *dir, const char *base)
- {
- char *dir_end;
- char *p_concat;
-
- assert (strlen (dir) > 0);
- p_concat = xmalloc (strlen (dir) + strlen (base) + 2);
- dir_end = stpcpy (p_concat, dir);
- if (*(dir_end - 1) == '/')
- --dir_end;
- else if (*base == '/')
- ++base;
- stpcpy (stpcpy (dir_end, "/"), base);
- return p_concat;
- }
-
- /* Scan the arguments, and copy each by calling copy.
- Return 0 if successful, 1 if any errors occur. */
-
- static int
- do_copy (int argc, char **argv)
- {
- char *dest;
- struct stat sb;
- int new_dst = 0;
- int ret = 0;
-
- if (optind >= argc)
- usage (2, _("missing file arguments"));
- if (optind >= argc - 1)
- usage (2, _("missing destination file"));
-
- dest = argv[argc - 1];
-
- if (lstat (dest, &sb))
- {
- if (errno != ENOENT)
- {
- error (0, errno, "%s", dest);
- return 1;
- }
- else
- new_dst = 1;
- }
- else
- {
- struct stat sbx;
-
- /* If `dest' is not a symlink to a nonexistent file, use
- the results of stat instead of lstat, so we can copy files
- into symlinks to directories. */
- if (stat (dest, &sbx) == 0)
- sb = sbx;
- }
-
- if (!new_dst && S_ISDIR (sb.st_mode))
- {
- /* cp file1...filen edir
- Copy the files `file1' through `filen'
- to the existing directory `edir'. */
-
- for (;;)
- {
- char *arg;
- char *ap;
- char *dst_path;
- int parent_exists = 1; /* True if dirname (dst_path) exists. */
- struct dir_attr *attr_list;
-
- arg = argv[optind];
-
- strip_trailing_slashes (arg);
-
- if (flag_path)
- {
- /* Append all of `arg' to `dest'. */
- dst_path = path_concat (dest, arg);
-
- /* For --parents, we have to make sure that the directory
- dirname (dst_path) exists. We may have to create a few
- leading directories. */
- parent_exists = !make_path_private (dst_path,
- strlen (dest) + 1, 0700,
- (flag_verbose
- ? "%s -> %s\n" : NULL),
- &attr_list, &new_dst);
- }
- else
- {
- /* Append the last component of `arg' to `dest'. */
-
- ap = basename (arg);
- /* For `cp -R source/.. dest', don't copy into `dest/..'. */
- dst_path = (STREQ (ap, "..")
- ? xstrdup (dest)
- : path_concat (dest, ap));
- }
-
- if (!parent_exists)
- {
- /* make_path_private failed, so don't even attempt the copy. */
- ret = 1;
- }
- else
- {
- ret |= copy (arg, dst_path, new_dst, 0, (struct dir_list *) 0);
- forget_all ();
-
- if (flag_path)
- {
- ret |= re_protect (dst_path, strlen (dest) + 1, attr_list);
- }
- }
-
- free (dst_path);
- ++optind;
- if (optind == argc - 1)
- break;
- }
- return ret;
- }
- else if (argc - optind == 2)
- {
- char *new_dest;
- char *source;
- struct stat source_stats;
-
- if (flag_path)
- usage (2,
- _("when preserving paths, last argument must be a directory"));
-
- source = argv[optind];
-
- /* When the force and backup options have been specified and
- the source and destination are the same name for an existing
- regular file, convert the user's command, e.g.,
- `cp --force --backup foo foo' to `cp --force foo fooSUFFIX'
- where SUFFIX is determined by any version control options used. */
-
- if (flag_force
- && backup_type != none
- && STREQ (source, dest)
- && !new_dst && S_ISREG (sb.st_mode))
- {
- backup_type = none;
- new_dest = find_backup_file_name (dest);
- if (new_dest == NULL)
- error (1, 0, _("virtual memory exhausted"));
- }
-
- /* When the destination is specified with a trailing slash and the
- source exists but is not a directory, convert the user's command
- `cp source dest/' to `cp source dest/basename(source)'. */
-
- else if (dest[strlen (dest) - 1] == '/'
- && lstat (source, &source_stats) == 0
- && !S_ISDIR (source_stats.st_mode))
- {
- char *source_base;
- char *tmp_source;
-
- tmp_source = (char *) alloca (strlen (source) + 1);
- strcpy (tmp_source, source);
- strip_trailing_slashes (tmp_source);
- source_base = basename (tmp_source);
-
- new_dest = (char *) alloca (strlen (dest) + 1 +
- strlen (source_base) + 1);
- stpcpy (stpcpy (stpcpy (new_dest, dest), "/"), source_base);
- }
- else
- {
- new_dest = dest;
- }
-
- return copy (source, new_dest, new_dst, 0, (struct dir_list *) 0);
- }
- else
- usage (2,
- _("when copying multiple files, last argument must be a directory"));
- }
-
- /* Copy the file SRC_PATH to the file DST_PATH. The files may be of
- any type. NEW_DST should be nonzero if the file DST_PATH cannot
- exist because its parent directory was just created; NEW_DST should
- be zero if DST_PATH might already exist. DEVICE is the device
- number of the parent directory, or 0 if the parent of this file is
- not known. ANCESTORS points to a linked, null terminated list of
- devices and inodes of parent directories of SRC_PATH.
- Return 0 if successful, 1 if an error occurs. */
-
- static int
- copy (const char *src_path, const char *dst_path, int new_dst, dev_t device,
- struct dir_list *ancestors)
- {
- struct stat src_sb;
- struct stat dst_sb;
- int src_mode;
- int src_type;
- char *earlier_file;
- char *dst_backup = NULL;
- int fix_mode = 0;
-
- if ((*xstat) (src_path, &src_sb))
- {
- error (0, errno, "%s", src_path);
- return 1;
- }
-
- /* Are we crossing a file system boundary? */
- if (flag_one_file_system && device != 0 && device != src_sb.st_dev)
- return 0;
-
- /* We wouldn't insert a node unless nlink > 1, except that we need to
- find created files so as to not copy infinitely if a directory is
- copied into itself. */
-
- earlier_file = remember_copied (dst_path, src_sb.st_ino, src_sb.st_dev);
-
- /* Did we just create this file? */
-
- if (earlier_file == &new_file)
- return 0;
-
- src_mode = src_sb.st_mode;
- src_type = src_sb.st_mode;
-
- if (S_ISDIR (src_type) && !flag_recursive)
- {
- error (0, 0, _("%s: omitting directory"), src_path);
- return 1;
- }
-
- if (!new_dst)
- {
- if ((*xstat) (dst_path, &dst_sb))
- {
- if (errno != ENOENT)
- {
- error (0, errno, "%s", dst_path);
- return 1;
- }
- else
- new_dst = 1;
- }
- else
- {
- /* The file exists already. */
-
- if (src_sb.st_ino == dst_sb.st_ino && src_sb.st_dev == dst_sb.st_dev)
- {
- if (flag_hard_link)
- return 0;
-
- error (0, 0, _("`%s' and `%s' are the same file"),
- src_path, dst_path);
- return 1;
- }
-
- if (!S_ISDIR (src_type))
- {
- if (S_ISDIR (dst_sb.st_mode))
- {
- error (0, 0,
- _("%s: cannot overwrite directory with non-directory"),
- dst_path);
- return 1;
- }
-
- if (flag_update && src_sb.st_mtime <= dst_sb.st_mtime)
- return 0;
- }
-
- if (S_ISREG (src_type) && !flag_force)
- {
- if (flag_interactive)
- {
- if (euidaccess (dst_path, W_OK) != 0)
- fprintf (stderr,
- _("%s: overwrite `%s', overriding mode %04o? "),
- program_name, dst_path,
- (unsigned int) (dst_sb.st_mode & 07777));
- else
- fprintf (stderr, _("%s: overwrite `%s'? "),
- program_name, dst_path);
- if (!yesno ())
- return 0;
- }
- }
-
- if (backup_type != none && !S_ISDIR (dst_sb.st_mode))
- {
- char *tmp_backup = find_backup_file_name (dst_path);
- if (tmp_backup == NULL)
- error (1, 0, _("virtual memory exhausted"));
-
- /* Detect (and fail) when creating the backup file would
- destroy the source file. Before, running the commands
- cd /tmp; rm -f a a~; : > a; echo A > a~; cp -b -V simple a~ a
- would leave two zero-length files: a and a~. */
- if (STREQ (tmp_backup, src_path))
- {
- error (0, 0,
- _("backing up `%s' would destroy source; `%s' not copied"),
- dst_path, src_path);
- return 1;
-
- }
- dst_backup = (char *) alloca (strlen (tmp_backup) + 1);
- strcpy (dst_backup, tmp_backup);
- free (tmp_backup);
- if (rename (dst_path, dst_backup))
- {
- if (errno != ENOENT)
- {
- error (0, errno, _("cannot backup `%s'"), dst_path);
- return 1;
- }
- else
- dst_backup = NULL;
- }
- new_dst = 1;
- }
- else if (flag_force)
- {
- if (S_ISDIR (dst_sb.st_mode))
- {
- /* Temporarily change mode to allow overwriting. */
- if (euidaccess (dst_path, W_OK | X_OK) != 0)
- {
- if (chmod (dst_path, 0700))
- {
- error (0, errno, "%s", dst_path);
- return 1;
- }
- else
- fix_mode = 1;
- }
- }
- else
- {
- if (unlink (dst_path) && errno != ENOENT)
- {
- error (0, errno, _("cannot remove old link to `%s'"),
- dst_path);
- return 1;
- }
- new_dst = 1;
- }
- }
- }
- }
-
- /* If the source is a directory, we don't always create the destination
- directory. So --verbose should not announce anything until we're
- sure we'll create a directory. */
- if (flag_verbose && !S_ISDIR (src_type))
- printf ("%s -> %s\n", src_path, dst_path);
-
- /* Did we copy this inode somewhere else (in this command line argument)
- and therefore this is a second hard link to the inode? */
-
- if (!flag_dereference && src_sb.st_nlink > 1 && earlier_file)
- {
- if (link (earlier_file, dst_path))
- {
- error (0, errno, "%s", dst_path);
- goto un_backup;
- }
- return 0;
- }
-
- if (S_ISDIR (src_type))
- {
- struct dir_list *dir;
-
- /* If this directory has been copied before during the
- recursion, there is a symbolic link to an ancestor
- directory of the symbolic link. It is impossible to
- continue to copy this, unless we've got an infinite disk. */
-
- if (is_ancestor (&src_sb, ancestors))
- {
- error (0, 0, _("%s: cannot copy cyclic symbolic link"), src_path);
- goto un_backup;
- }
-
- /* Insert the current directory in the list of parents. */
-
- dir = (struct dir_list *) alloca (sizeof (struct dir_list));
- dir->parent = ancestors;
- dir->ino = src_sb.st_ino;
- dir->dev = src_sb.st_dev;
-
- if (new_dst || !S_ISDIR (dst_sb.st_mode))
- {
- /* Create the new directory writable and searchable, so
- we can create new entries in it. */
-
- if (mkdir (dst_path, (src_mode & umask_kill) | 0700))
- {
- error (0, errno, _("cannot create directory `%s'"), dst_path);
- goto un_backup;
- }
-
- /* Insert the created directory's inode and device
- numbers into the search structure, so that we can
- avoid copying it again. */
-
- if (remember_created (dst_path))
- goto un_backup;
-
- if (flag_verbose)
- printf ("%s -> %s\n", src_path, dst_path);
- }
-
- /* Copy the contents of the directory. */
-
- if (copy_dir (src_path, dst_path, new_dst, &src_sb, dir))
- return 1;
- }
- #ifdef S_ISLNK
- else if (flag_symbolic_link)
- {
- if (*src_path == '/'
- || (!strncmp (dst_path, "./", 2) && strchr (dst_path + 2, '/') == 0)
- || strchr (dst_path, '/') == 0)
- {
- if (symlink (src_path, dst_path))
- {
- error (0, errno, "%s", dst_path);
- goto un_backup;
- }
- return 0;
- }
- else
- {
- error (0, 0,
- _("%s: can make relative symbolic links only in current directory"),
- dst_path);
- goto un_backup;
- }
- }
- #endif
- else if (flag_hard_link)
- {
- if (link (src_path, dst_path))
- {
- error (0, errno, _("cannot create link `%s'"), dst_path);
- goto un_backup;
- }
- return 0;
- }
- else if (S_ISREG (src_type)
- || (flag_copy_as_regular && !S_ISDIR (src_type)
- #ifdef S_ISLNK
- && !S_ISLNK (src_type)
- #endif
- ))
- {
- if (copy_reg (src_path, dst_path))
- goto un_backup;
- }
- else
- #ifdef S_ISFIFO
- if (S_ISFIFO (src_type))
- {
- if (mkfifo (dst_path, src_mode & umask_kill))
- {
- error (0, errno, _("cannot create fifo `%s'"), dst_path);
- goto un_backup;
- }
- }
- else
- #endif
- if (S_ISBLK (src_type) || S_ISCHR (src_type)
- #ifdef S_ISSOCK
- || S_ISSOCK (src_type)
- #endif
- )
- {
- if (mknod (dst_path, src_mode & umask_kill, src_sb.st_rdev))
- {
- error (0, errno, _("cannot create special file `%s'"), dst_path);
- goto un_backup;
- }
- }
- else
- #ifdef S_ISLNK
- if (S_ISLNK (src_type))
- {
- char *link_val;
- int link_size;
-
- link_val = (char *) alloca (PATH_MAX + 2);
- link_size = readlink (src_path, link_val, PATH_MAX + 1);
- if (link_size < 0)
- {
- error (0, errno, _("cannot read symbolic link `%s'"), src_path);
- goto un_backup;
- }
- link_val[link_size] = '\0';
-
- if (symlink (link_val, dst_path))
- {
- error (0, errno, _("cannot create symbolic link `%s'"), dst_path);
- goto un_backup;
- }
- return 0;
- }
- else
- #endif
- {
- error (0, 0, _("%s: unknown file type"), src_path);
- goto un_backup;
- }
-
- /* Adjust the times (and if possible, ownership) for the copy.
- chown turns off set[ug]id bits for non-root,
- so do the chmod last. */
-
- if (flag_preserve)
- {
- struct utimbuf utb;
-
- utb.actime = src_sb.st_atime;
- utb.modtime = src_sb.st_mtime;
-
- if (utime (dst_path, &utb))
- {
- error (0, errno, "%s", dst_path);
- return 1;
- }
-
- /* If non-root uses -p, it's ok if we can't preserve ownership.
- But root probably wants to know, e.g. if NFS disallows it. */
- if (chown (dst_path,
- (myeuid == 0 ? src_sb.st_uid : myeuid),
- src_sb.st_gid)
- && (errno != EPERM || myeuid == 0))
- {
- error (0, errno, "%s", dst_path);
- return 1;
- }
- }
-
- if ((flag_preserve || new_dst)
- && (flag_copy_as_regular || S_ISREG (src_type) || S_ISDIR (src_type)))
- {
- if (chmod (dst_path, src_mode & umask_kill))
- {
- error (0, errno, "%s", dst_path);
- return 1;
- }
- }
- else if (fix_mode)
- {
- /* Reset the temporarily changed mode. */
- if (chmod (dst_path, dst_sb.st_mode))
- {
- error (0, errno, "%s", dst_path);
- return 1;
- }
- }
-
- return 0;
-
- un_backup:
- if (dst_backup)
- {
- if (rename (dst_backup, dst_path))
- error (0, errno, _("cannot un-backup `%s'"), dst_path);
- }
- return 1;
- }
-
- /* Ensure that the parent directory of CONST_DIRPATH exists, for
- the --parents option.
-
- SRC_OFFSET is the index in CONST_DIRPATH (which is a destination
- path) of the beginning of the source directory name.
- Create any leading directories that don't already exist,
- giving them permissions MODE.
- If VERBOSE_FMT_STRING is nonzero, use it as a printf format
- string for printing a message after successfully making a directory.
- The format should take two string arguments: the names of the
- source and destination directories.
- Creates a linked list of attributes of intermediate directories,
- *ATTR_LIST, for re_protect to use after calling copy.
- Sets *NEW_DST to 1 if this function creates parent of CONST_DIRPATH.
-
- Return 0 if parent of CONST_DIRPATH exists as a directory with the proper
- permissions when done, otherwise 1. */
-
- static int
- make_path_private (const char *const_dirpath, int src_offset, int mode,
- const char *verbose_fmt_string, struct dir_attr **attr_list,
- int *new_dst)
- {
- struct stat stats;
- char *dirpath; /* A copy of CONST_DIRPATH we can change. */
- char *src; /* Source name in `dirpath'. */
- char *tmp_dst_dirname; /* Leading path of `dirpath', malloc. */
- char *dst_dirname; /* Leading path of `dirpath', alloca. */
-
- dirpath = (char *) alloca (strlen (const_dirpath) + 1);
- strcpy (dirpath, const_dirpath);
-
- src = dirpath + src_offset;
-
- tmp_dst_dirname = dirname (dirpath);
- dst_dirname = (char *) alloca (strlen (tmp_dst_dirname) + 1);
- strcpy (dst_dirname, tmp_dst_dirname);
- free (tmp_dst_dirname);
-
- *attr_list = NULL;
-
- if ((*xstat) (dst_dirname, &stats))
- {
- /* Parent of CONST_DIRNAME does not exist.
- Make all missing intermediate directories. */
- char *slash;
-
- slash = src;
- while (*slash == '/')
- slash++;
- while ((slash = strchr (slash, '/')))
- {
- /* Add this directory to the list of directories whose modes need
- fixing later. */
- struct dir_attr *new =
- (struct dir_attr *) xmalloc (sizeof (struct dir_attr));
- new->slash_offset = slash - dirpath;
- new->next = *attr_list;
- *attr_list = new;
-
- *slash = '\0';
- if ((*xstat) (dirpath, &stats))
- {
- /* This element of the path does not exist. We must set
- *new_dst and new->is_new_dir inside this loop because,
- for example, in the command `cp --parents ../a/../b/c e_dir',
- make_path_private creates only e_dir/../a if ./b already
- exists. */
- *new_dst = 1;
- new->is_new_dir = 1;
- if (mkdir (dirpath, mode))
- {
- error (0, errno, _("cannot make directory `%s'"), dirpath);
- return 1;
- }
- else
- {
- if (verbose_fmt_string != NULL)
- printf (verbose_fmt_string, src, dirpath);
- }
- }
- else if (!S_ISDIR (stats.st_mode))
- {
- error (0, 0, _("`%s' exists but is not a directory"), dirpath);
- return 1;
- }
- else
- {
- new->is_new_dir = 0;
- *new_dst = 0;
- }
- *slash++ = '/';
-
- /* Avoid unnecessary calls to `stat' when given
- pathnames containing multiple adjacent slashes. */
- while (*slash == '/')
- slash++;
- }
- }
-
- /* We get here if the parent of `dirpath' already exists. */
-
- else if (!S_ISDIR (stats.st_mode))
- {
- error (0, 0, _("`%s' exists but is not a directory"), dst_dirname);
- return 1;
- }
- else
- {
- *new_dst = 0;
- }
- return 0;
- }
-
- /* Ensure that the parent directories of CONST_DST_PATH have the
- correct protections, for the --parents option. This is done
- after all copying has been completed, to allow permissions
- that don't include user write/execute.
-
- SRC_OFFSET is the index in CONST_DST_PATH of the beginning of the
- source directory name.
-
- ATTR_LIST is a null-terminated linked list of structures that
- indicates the end of the filename of each intermediate directory
- in CONST_DST_PATH that may need to have its attributes changed.
- The command `cp --parents --preserve a/b/c d/e_dir' changes the
- attributes of the directories d/e_dir/a and d/e_dir/a/b to match
- the corresponding source directories regardless of whether they
- existed before the `cp' command was given.
-
- Return 0 if the parent of CONST_DST_PATH and any intermediate
- directories specified by ATTR_LIST have the proper permissions
- when done, otherwise 1. */
-
- static int
- re_protect (const char *const_dst_path, int src_offset,
- struct dir_attr *attr_list)
- {
- struct dir_attr *p;
- char *dst_path; /* A copy of CONST_DST_PATH we can change. */
- char *src_path; /* The source name in `dst_path'. */
-
- dst_path = (char *) alloca (strlen (const_dst_path) + 1);
- strcpy (dst_path, const_dst_path);
- src_path = dst_path + src_offset;
-
- for (p = attr_list; p; p = p->next)
- {
- struct stat src_sb;
-
- dst_path[p->slash_offset] = '\0';
-
- if ((*xstat) (src_path, &src_sb))
- {
- error (0, errno, "%s", src_path);
- return 1;
- }
-
- /* Adjust the times (and if possible, ownership) for the copy.
- chown turns off set[ug]id bits for non-root,
- so do the chmod last. */
-
- if (flag_preserve)
- {
- struct utimbuf utb;
-
- utb.actime = src_sb.st_atime;
- utb.modtime = src_sb.st_mtime;
-
- if (utime (dst_path, &utb))
- {
- error (0, errno, "%s", dst_path);
- return 1;
- }
-
- /* If non-root uses -p, it's ok if we can't preserve ownership.
- But root probably wants to know, e.g. if NFS disallows it. */
- if (chown (dst_path, src_sb.st_uid, src_sb.st_gid)
- && (errno != EPERM || myeuid == 0))
- {
- error (0, errno, "%s", dst_path);
- return 1;
- }
- }
-
- if (flag_preserve || p->is_new_dir)
- {
- if (chmod (dst_path, src_sb.st_mode & umask_kill))
- {
- error (0, errno, "%s", dst_path);
- return 1;
- }
- }
-
- dst_path[p->slash_offset] = '/';
- }
- return 0;
- }
-
- /* Read the contents of the directory SRC_PATH_IN, and recursively
- copy the contents to DST_PATH_IN. NEW_DST is nonzero if
- DST_PATH_IN is a directory that was created previously in the
- recursion. SRC_SB and ANCESTORS describe SRC_PATH_IN.
- Return 0 if successful, -1 if an error occurs. */
-
- static int
- copy_dir (const char *src_path_in, const char *dst_path_in, int new_dst,
- const struct stat *src_sb, struct dir_list *ancestors)
- {
- char *name_space;
- char *namep;
- char *src_path;
- char *dst_path;
- int ret = 0;
-
- errno = 0;
- name_space = savedir (src_path_in, src_sb->st_size);
- if (name_space == 0)
- {
- if (errno)
- {
- error (0, errno, "%s", src_path_in);
- return -1;
- }
- else
- error (1, 0, _("virtual memory exhausted"));
- }
-
- namep = name_space;
- while (*namep != '\0')
- {
- int fn_length = strlen (namep) + 1;
-
- dst_path = xmalloc (strlen (dst_path_in) + fn_length + 1);
- src_path = xmalloc (strlen (src_path_in) + fn_length + 1);
-
- stpcpy (stpcpy (stpcpy (src_path, src_path_in), "/"), namep);
- stpcpy (stpcpy (stpcpy (dst_path, dst_path_in), "/"), namep);
-
- ret |= copy (src_path, dst_path, new_dst, src_sb->st_dev, ancestors);
-
- /* Free the memory for `src_path'. The memory for `dst_path'
- cannot be deallocated, since it is used to create multiple
- hard links. */
-
- free (src_path);
-
- namep += fn_length;
- }
- free (name_space);
- return -ret;
- }
-
- /* Copy a regular file from SRC_PATH to DST_PATH.
- If the source file contains holes, copies holes and blocks of zeros
- in the source file as holes in the destination file.
- (Holes are read as zeroes by the `read' system call.)
- Return 0 if successful, -1 if an error occurred. */
-
- static int
- copy_reg (const char *src_path, const char *dst_path)
- {
- char *buf;
- int buf_size;
- int dest_desc;
- int source_desc;
- int n_read;
- struct stat sb;
- char *cp;
- int *ip;
- int return_val = 0;
- long n_read_total = 0;
- int last_write_made_hole = 0;
- int make_holes = (flag_sparse == SPARSE_ALWAYS);
-
- source_desc = open (src_path, O_RDONLY);
- if (source_desc < 0)
- {
- error (0, errno, "%s", src_path);
- return -1;
- }
-
- /* Create the new regular file with small permissions initially,
- to not create a security hole. */
-
- dest_desc = open (dst_path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
- if (dest_desc < 0)
- {
- error (0, errno, _("cannot create regular file `%s'"), dst_path);
- return_val = -1;
- goto ret2;
- }
-
- /* Find out the optimal buffer size. */
-
- if (fstat (dest_desc, &sb))
- {
- error (0, errno, "%s", dst_path);
- return_val = -1;
- goto ret;
- }
-
- buf_size = ST_BLKSIZE (sb);
-
- #ifdef HAVE_ST_BLOCKS
- if (flag_sparse == SPARSE_AUTO && S_ISREG (sb.st_mode))
- {
- /* Use a heuristic to determine whether SRC_PATH contains any
- sparse blocks. */
-
- if (fstat (source_desc, &sb))
- {
- error (0, errno, "%s", src_path);
- return_val = -1;
- goto ret;
- }
-
- /* If the file has fewer blocks than would normally
- be needed for a file of its size, then
- at least one of the blocks in the file is a hole. */
- if (S_ISREG (sb.st_mode)
- && (size_t) (sb.st_size / 512) > (size_t) ST_NBLOCKS (sb))
- make_holes = 1;
- }
- #endif
-
- /* Make a buffer with space for a sentinel at the end. */
-
- buf = (char *) alloca (buf_size + sizeof (int));
-
- for (;;)
- {
- n_read = read (source_desc, buf, buf_size);
- if (n_read < 0)
- {
- #ifdef EINTR
- if (errno == EINTR)
- continue;
- #endif
- error (0, errno, "%s", src_path);
- return_val = -1;
- goto ret;
- }
- if (n_read == 0)
- break;
-
- n_read_total += n_read;
-
- ip = 0;
- if (make_holes)
- {
- buf[n_read] = 1; /* Sentinel to stop loop. */
-
- /* Find first nonzero *word*, or the word with the sentinel. */
-
- ip = (int *) buf;
- while (*ip++ == 0)
- ;
-
- /* Find the first nonzero *byte*, or the sentinel. */
-
- cp = (char *) (ip - 1);
- while (*cp++ == 0)
- ;
-
- /* If we found the sentinel, the whole input block was zero,
- and we can make a hole. */
-
- if (cp > buf + n_read)
- {
- /* Make a hole. */
- if (lseek (dest_desc, (off_t) n_read, SEEK_CUR) < 0L)
- {
- error (0, errno, "%s", dst_path);
- return_val = -1;
- goto ret;
- }
- last_write_made_hole = 1;
- }
- else
- /* Clear to indicate that a normal write is needed. */
- ip = 0;
- }
- if (ip == 0)
- {
- if (full_write (dest_desc, buf, n_read) < 0)
- {
- error (0, errno, "%s", dst_path);
- return_val = -1;
- goto ret;
- }
- last_write_made_hole = 0;
- }
- }
-
- /* If the file ends with a `hole', something needs to be written at
- the end. Otherwise the kernel would truncate the file at the end
- of the last write operation. */
-
- if (last_write_made_hole)
- {
- #ifdef HAVE_FTRUNCATE
- /* Write a null character and truncate it again. */
- if (full_write (dest_desc, "", 1) < 0
- || ftruncate (dest_desc, n_read_total) < 0)
- #else
- /* Seek backwards one character and write a null. */
- if (lseek (dest_desc, (off_t) -1, SEEK_CUR) < 0L
- || full_write (dest_desc, "", 1) < 0)
- #endif
- {
- error (0, errno, "%s", dst_path);
- return_val = -1;
- }
- }
-
- ret:
- if (close (dest_desc) < 0)
- {
- error (0, errno, "%s", dst_path);
- return_val = -1;
- }
- ret2:
- if (close (source_desc) < 0)
- {
- error (0, errno, "%s", src_path);
- return_val = -1;
- }
-
- return return_val;
- }
-